package com.jxud.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jxud.service.OrderService;
import com.jxud.service.impl.OrderServiceImpi;
import com.jxud.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 增强版 AOP 日志切面
 * 支持：TraceID、JSON 格式化、敏感字段过滤、用户信息等
 */
@Slf4j
@Aspect
@Component
public class LogAopAspect {

    @Autowired
    private ObjectMapper objectMapper; // 用于 JSON 序列

    // 敏感字段，输出日志时用 *** 替换
    private static final String[] SENSITIVE_FIELDS = {"password", "pwd", "token", "secret", "phone", "mobile", "idCard"};
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderServiceImpi orderServiceImpi;

    /**
     * 定义切入点：拦截 controller 包下所有类的所有公共方法
     */
    @Pointcut("execution(public * com.jxud.controller..*.*(..))")
    public void controllerLog() {}

    /**
     * 前置通知：记录请求开始
     */
    @Before("controllerLog()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return;
        HttpServletRequest request = attributes.getRequest();

        String token = request.getHeader("token");

        String wxName;
        if (token != null && !token.trim().isEmpty()) {
            try {
                Claims claims = JwtUtils.parseJwt(token);
                wxName = (String) claims.get("wxName");
            } catch (Exception e) {
                // ✅ 解析失败（token 过期、格式错误等），不抛异常，只记录 warn 日志
                log.warn("Token 解析失败，可能已过期或不合法: {}", token, e);
                wxName = "Token 解析失败，可能已过期或不合法";
            }
        } else {
            // ✅ 没有 token，说明是登录、注册、验证码等公开接口
            log.warn("不需要token的接口");
            wxName = "不需要token的接口";
        }


        // 生成或获取 TraceID
        String traceId = getOrCreateTraceId(request);
        request.setAttribute("TRACE_ID", traceId);

        String method = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringTypeName();

        log.info("""
                
                ============ 🌟 请求开始 [{}] ============
                📌 TRACE_ID : {}
                🌐 请求URL   : {}
                🔗 方法      : {}.{}
                📱  客户端IP  : {}
                📡 请求方式   : {}
                💾 请求参数   : {}
                👳‍♂️ 用户      ： {}
                ============ 🚀 处理中... ============""" ,
                traceId,
                traceId,
                request.getRequestURL().toString(),
                className,
                method,
                getClientIp(request),
                request.getMethod(),
                formatArgs(joinPoint.getArgs()),
                wxName
        );
    }

    /**
     * 环绕通知：记录执行耗时和返回结果
     */
    @Around("controllerLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String traceId = (String) RequestContextHolder.getRequestAttributes().getAttribute("TRACE_ID", 0);

        Object result;
        try {
            result = joinPoint.proceed(); // 执行业务方法
        } catch (Exception e) {
            log.error("❌ 请求执行异常 [{}]", traceId, e);
            throw e;
        }

        long duration = System.currentTimeMillis() - startTime;

        log.info("""
                ============ ✅ 请求完成 [{}] ============
                📌 TRACE_ID : {}
                🕒 耗时(ms)  : {}
                📤 响应结果 : {}
                ============ 🎉 结束 ============""" ,
                traceId,
                traceId,
                duration,
                formatObject(result)
        );

        return result;
    }

    /**
     * 异常通知：记录异常信息
     */
    @AfterThrowing(pointcut = "controllerLog()", throwing = "e")
    public void doAfterThrowing(Exception e) {
        String traceId = getTraceId();
        log.error("🔥 接口抛出异常 [{}]: {}", traceId, e.getMessage(), e);
    }

    // ==================== 工具方法 ====================

    /**
     * 获取或创建 TraceID
     */
    private String getOrCreateTraceId(HttpServletRequest request) {
        String traceId = request.getHeader("X-Trace-ID");
        if (traceId == null || traceId.isEmpty()) {
            traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16); // 16位
        }
        return traceId;
    }

    /**
     * 获取真实 IP（兼容反向代理）
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.isEmpty()) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 格式化方法参数（支持对象、List、Map、MultipartFile 等）
     */
    private String formatArgs(Object[] args) {
        if (args == null || args.length == 0) return "[]";

        Object[] formatted = Arrays.stream(args)
                .map(this::formatObject)
                .toArray();

        return Arrays.toString(formatted);
    }

    /**
     * 将对象转为 JSON 字符串（隐藏敏感字段）
     */
    private String formatObject(Object obj) {
        if (obj == null) return "null";

        // 特殊类型处理
        if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) {
            return obj.toString();
        }
        if (obj instanceof MultipartFile) {
            return "MultipartFile[name=" + ((MultipartFile) obj).getOriginalFilename() + "]";
        }
        if (obj instanceof HttpServletResponse) {
            return "HttpServletResponse";
        }

        // 其他对象转 JSON 并过滤敏感字段
        try {
            String json = objectMapper.writeValueAsString(obj);
            return maskSensitiveFields(json);
        } catch (JsonProcessingException e) {
            return "JSON_SERIALIZE_ERROR: " + obj.getClass().getSimpleName();
        }
    }

    /**
     * 简单过滤敏感字段（基于 JSON 字符串替换）
     */
    private String maskSensitiveFields(String json) {
        String masked = json;
        for (String field : SENSITIVE_FIELDS) {
            masked = masked.replaceAll("(\"" + field + "\":\\s*\")[^\"]+\"", "$1***\"");
        }
        return masked;
    }

    /**
     * 获取当前 TraceID
     */
    private String getTraceId() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            return (String) attributes.getRequest().getAttribute("TRACE_ID");
        }
        return "UNKNOWN";
    }
}